/** @file bt_cmd.c
  *
  * @brief This file contains the Marvell Bluetooth Command
  * handling functions of Bluetooth driver.
  * 
  * (c) Copyright 2003-2007, Marvell International Ltd.
  * All Rights Reserved
  */

#include <windows.h>

#include "bt_def.h"
#include "bt_dev.h"
#include "bt_func.h"
#include "igxBug.h"


/********************************************************
        Local Variables
********************************************************/

/********************************************************
        Global Variables
********************************************************/

/********************************************************
        Local Functions
********************************************************/

/********************************************************
        Global Functions
********************************************************/
MRVL_STATUS SendBTCommand(PMRVDRV_ADAPTER Adapter,
                          UCHAR option,
                          UCHAR cmd,
                          UCHAR subcmd0,
                          UCHAR subcmd1)
{
    MRVL_STATUS ret = MRVL_STATUS_SUCCESS;
    MRVL_STATUS Status;
    BT_CMD *pCmd;
    SD_TRANSPORT_HEADER SDIO_header;
    PUCHAR pCmdBuffer = NULL;
    UCHAR  BTCmdSize;
    DWORD  waitStatus;

    BTCmdSize = SDIO_HEADER_LEN + sizeof(BT_CMD);

    pCmdBuffer = (PUCHAR)MRVDRV_ALLOC_MEM(BTCmdSize);
    if (!pCmdBuffer)
    {
        DBGPRINT(DBG_CMD|DBG_ERROR, (L"Memory Allocation Fail !\n\r"));
        ret = MRVL_STATUS_FAILURE;
        goto exit;
    }

    /* Fill in SDIO Packet Header */
    SDIO_header.u.AsULONG = BTCmdSize;
    SDIO_header.u.AsUCHAR.ServiceID = MRVL_VENDOR_PKT;
    memmove(pCmdBuffer, &SDIO_header, SDIO_HEADER_LEN);

    /* Fill in BT Command */
    pCmd = (BT_CMD *)&pCmdBuffer[SDIO_HEADER_LEN];      
    pCmd->ocf_ogf =  ((OGF << 10) | (cmd));
    switch (cmd)
    {
        case BT_CMD_AUTO_SLEEP_MODE:
            pCmd->length = 1;
            pCmd->data[0] = subcmd0;
            pCmd->data[1] = 0;
            break;
        case BT_CMD_HOST_SLEEP_CONFIG:
            pCmd->length = 2;
            pCmd->data[0] = subcmd0;    /* wakeup method */
            pCmd->data[1] = subcmd1;    /* GAP value */
            pCmd->data[2] = 0;
            pCmd->data[3] = 0;
            break;
        case BT_CMD_HOST_SLEEP_ENABLE:
            pCmd->length = 0;
            pCmd->data[0] = 0;
            pCmd->data[1] = 0;
            pCmd->data[2] = 0;
            pCmd->data[3] = 0;
            break;
        case BT_CMD_MODULE_CFG_REQ:
            if (subcmd0 == MODULE_INTERFACE_CTRL_REQ)
            {
                pCmd->length = 3;
                pCmd->data[0] = subcmd0;
                pCmd->data[1] = subcmd1;
                pCmd->data[2] = 2;      /* Interface used by BT */
                pCmd->data[3] = 0;
            }
            else
            {
                pCmd->length = 1;
                pCmd->data[0] = subcmd0;
                pCmd->data[1] = 0;
                pCmd->data[2] = 0;
                pCmd->data[3] = 0;
            }
            break;
        default:
            DBGPRINT(DBG_CMD|DBG_ERROR, (L"Unknown BT Vendor Command !\n\r"));
            ret = MRVL_STATUS_FAILURE;
            goto exit;
    }

    DBGPRINT(DBG_CMD, (L"Send Command : 0x%02x\n\r", cmd));
    HexDump(DBG_CMD_DUMP, "CMD BUFFER : ", (PUCHAR)pCmdBuffer, BTCmdSize);

    Status = SendSinglePacket(pCmdBuffer, BTCmdSize);
    if (Status != MRVL_STATUS_SUCCESS)
    {
        DBGPRINT(DBG_CMD, (L"Send Command FAIL !\n\r"));
        ret = MRVL_STATUS_FAILURE;
        goto exit;
    }
    else
    {
        DBGPRINT(DBG_CMD, (L"Send Command OK !\n\r"));
    }

    if (option == CMD_OPTION_WAIT_RESP)
    {
        Adapter->bWaitForCmd = TRUE;
        Adapter->CurrentCmd = cmd;

        /* Wait for command response */
        DBGPRINT(DBG_CMD, (L"Wait for Command Response : 0x%02x\n\r", Adapter->CurrentCmd));
        waitStatus = WaitForSingleObject(Adapter->hCmdRespEvent, WAIT_CMD_RESP_TIMEOUT);

        /* Wait complete */
        Adapter->bWaitForCmd = FALSE;

        switch (waitStatus)
        {
            case WAIT_OBJECT_0:
                DBGPRINT(DBG_CMD, (L"Command Response 0x%02x received.\n\r", Adapter->CurrentCmd));
                break;
            case WAIT_TIMEOUT:
                DBGPRINT(DBG_CMD|DBG_ERROR, (L"Wait Command Response timeout !\n\r"));
                ret = MRVL_STATUS_FAILURE;
                goto exit;
        }
    }

exit:
    if (pCmdBuffer)
    {
        MRVDRV_FREE_MEM((PVOID)pCmdBuffer);
    }

    return ret;
}


MRVL_STATUS HandleBTVendorPacket(PMRVDRV_ADAPTER Adapter,
                                 PUCHAR pBuffer)
{
    MRVL_STATUS ret = MRVL_STATUS_SUCCESS;
    BT_EVENT * pEvent;  

    pEvent = (BT_EVENT *)pBuffer;
    if (pEvent->EC != 0xff)
    {
        DBGPRINT(DBG_CMD, (L"Not Marvell Event=%x\n\r", pEvent->EC));
        ret = MRVL_STATUS_FAILURE;
        goto exit;
    }
 
    DBGPRINT(DBG_CMD, (L"Event ID : 0x%02x\n\r", pEvent->data[0]));
    HexDump(DBG_CMD_DUMP, "VENDOR EVENT BUFFER : ", pBuffer, sizeof(BT_EVENT));

    switch (pEvent->data[0])
    {
        case BT_CMD_AUTO_SLEEP_MODE:
            if (pEvent->data[2] == BT_CMD_SUCCESS)
            {
                if (pEvent->data[1] == BT_PS_ENABLE)
                    Adapter->psmode = 1;
                else
                    Adapter->psmode = 0;
                DBGPRINT(DBG_CMD, (L"PS Mode:%s\n\r",
                         (Adapter->psmode)?TEXT("Enable"):TEXT("Disable")));
            }
            else
            {
                DBGPRINT(DBG_CMD, (L"PS Mode Cmd Fail !\n\r"));
            }
            break;

        case BT_CMD_HOST_SLEEP_CONFIG:
            if (pEvent->data[3] == BT_CMD_SUCCESS)
            {
                DBGPRINT(DBG_CMD, (L"gpio=%x, gap=%x\n\r",
                         pEvent->data[1], pEvent->data[2]));
            }
            else
            {
                DBGPRINT(DBG_CMD, (L"HSCFG Cmd Fail\n\r"));
            }
            break;

        case BT_CMD_HOST_SLEEP_ENABLE:          
            if (pEvent->data[1] == BT_CMD_SUCCESS)
            {
                Adapter->hs_state = HS_ACTIVATED;
                if (Adapter->psmode)
                    Adapter->ps_state = PS_SLEEP;
                DBGPRINT(DBG_CMD, (L"HS ACTIVATED!\n\r"));
            }
            else
            {
                DBGPRINT(DBG_CMD, (L"HS Enable Fail\n\r"));
            }
            break;

        case  BT_CMD_MODULE_CFG_REQ:
            if ((pEvent->data[1] == MODULE_BRINGUP_REQ) ||
                (pEvent->data[1] == MODULE_SHUTDOWN_REQ))
            {
                if (pEvent->data[1] == MODULE_BRINGUP_REQ)
                {
                    if (pEvent->data[2] == 0x0c)
                    {
                        DBGPRINT(DBG_CMD, (L"EVENT:%s\n\r", TEXT("BT module is up and running")));
                    }
                    else
                    {
                        DBGPRINT(DBG_CMD, (L"EVENT:%s\n\r",
                                 (pEvent->data[2])?TEXT("Bring up Fail"):TEXT("Bring up success")));
                    }
                }
                if (pEvent->data[1] == MODULE_SHUTDOWN_REQ)
                {
                    DBGPRINT(DBG_CMD, (L"EVENT:%s\n\r",
                             (pEvent->data[2])?TEXT("Shut down Fail"):TEXT("Shut down success")));
                }
            }
            else
            {
                DBGPRINT(DBG_CMD, (L"BT_CMD_MODULE_CFG_REQ resp for APP\n\r"));
                ret = MRVL_STATUS_FAILURE;
            }
            break;

        case BT_EVENT_POWER_STATE:
            if (pEvent->data[1] == BT_PS_SLEEP)
            {
                Adapter->ps_state = PS_SLEEP;
            }
            else if (pEvent->data[1] == BT_PS_AWAKE)
            {
                Adapter->ps_state = PS_AWAKE;
                SetEvent(Adapter->hPSAwakeEvent);
            }
            DBGPRINT(DBG_CMD|DBG_PS, (L"EVENT:%s\n\r",
                    (Adapter->ps_state) ? TEXT("PS_SLEEP") : TEXT("PS_AWAKE")));
            break;

        default:
            DBGPRINT(DBG_CMD, (L"Unknown Event=%d\n\r", pEvent->data[0]));
            ret = MRVL_STATUS_FAILURE;
            break;          
    }

    if (Adapter->bWaitForCmd == TRUE && 
        pEvent->data[0] == Adapter->CurrentCmd)
    {
        SetEvent(Adapter->hCmdRespEvent);
    }

exit:       
    return ret;
}
